﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using System.Xml;

using mvcForum.Core;
using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Core.Interfaces;
using mvcForum.Core.Specifications;

using mvcForum.Web.Controllers;
using mvcForum.Web.ViewModels;
using mvcForum.Web.Events;
using mvcForum.Web.Helpers;
using mvcForum.Core.Interfaces.Search;
using mvcForum.Web.Interfaces;

using IMembershipService = mvcForum.Core.Interfaces.Services.IMembershipService;
using IAccount = mvcForum.Core.Interfaces.Services.IAccount;
using mvcForum.Core.Interfaces.Services;
using mvcForum.Core.Interfaces.Data;
using mvcForum.Core.Interfaces.Events;

namespace mvcForum.Web.Areas.ForumAdmin.Controllers {

	public class BasicInstallController : ForumBaseController {
		private readonly IConfiguration config;
		private readonly IEnumerable<IInstallable> installers;
		private readonly IMembershipService membershipService;

		public BasicInstallController(IConfiguration config,
										IContext context,
										IWebUserProvider userProvider,
										IEnumerable<IInstallable> installers,
										IMembershipService membershipService)
			: base(userProvider, context) {

			this.config = config;
			this.installers = installers;
			this.membershipService = membershipService;
		}

		public ActionResult Index() {
			IRepository<ForumSettings> settingsRepo = this.Context.GetRepository<ForumSettings>();
			try {
				var setting = settingsRepo.ReadOne(new ForumSettingsSpecifications.SpecificKey("Version"));
				if (setting != null) {
					return RedirectToAction("Index", "Home", new { area = "Forum" });
				}
			}
			catch { }

			BasicInstallViewModel model = new BasicInstallViewModel { CanCreateAdmin = true, CanCreateGroups = true, CanCreateSimpleForum = true };
			try {
				ForumUser user = this.ForumUserRepository.ReadOne(new ForumUserSpecifications.SpecificUsername("Administrator"));
				if (user != null) {
					model.CanCreateAdmin = false;
				}
			}
			catch { }

			IRepository<Group> groupRepo = this.Context.GetRepository<Group>();
			try {
				IEnumerable<Group> groups = groupRepo.ReadAll();
				if (groups.Count() > 0) {
					model.CanCreateGroups = false;
				}
			}
			catch { }

			IRepository<Board> boardRepo = this.Context.GetRepository<Board>();
			try {
				var boards = boardRepo.ReadAll();
				if (boards.Count() > 0) {
					model.CanCreateSimpleForum = false;
				}
			}
			catch { }

			model.SubmitInstallation = !Request.IsLocal;

			return View(model);
		}

		[HttpPost]
		public ActionResult Index(BasicInstallViewModel model) {
			if (ModelState.IsValid) {
				try {
					IRepository<ForumSettings> settingsRepo = this.Context.GetRepository<ForumSettings>();
					try {
						var setting = settingsRepo.ReadOne(new ForumSettingsSpecifications.SpecificKey("Version"));
						if (setting != null) {
							return RedirectToAction("Index", "Home", new { area = "Forum" });
						}
					}
					catch {
						// TODO: Move this somewhere else??
						//foreach (var installer in types.Installers) {
						//    installer.BeforeInstall(resolver);
						//}
					}

					try {
						// We don't have a database. so let's make sure there's no Lucene index lying around!
						// TODO: Clear this through the indexer!!
						foreach (IIndexer indexer in DependencyResolver.Current.GetServices<IIndexer>()) {
							indexer.Clear();
						}
					}
					catch (Exception e) { }


					this.config.DefaultCulture = "en-GB";
					this.config.DefaultTimezone = "GMT Standard Time";
					this.config.MessagesPerPage = 20;
					this.config.TopicsPerPage = 15;
					this.config.CheckForNews = true;
					this.config.Editor = "Regular";

					ForumUser admin = null;
					if (model.CreateAdmin && !model.ImportMembershipUsers) {
						String username = "Administrator";
						String password = "123456";
						String email = "admin@bogus.com";

						if (!String.IsNullOrWhiteSpace(model.AdminUsername)) {
							username = model.AdminUsername;
						}
						if (!String.IsNullOrWhiteSpace(model.AdminPassword)) {
							password = model.AdminPassword;
						}
						if (!String.IsNullOrWhiteSpace(model.AdminEmail)) {
							email = model.AdminEmail;
						}

						this.membershipService.CreateRole(Constants.Roles.BoardAdmin);
						this.membershipService.CreateRole(Constants.Roles.SolutionAdmin);

						String errorMessage;
						this.membershipService.CreateAccount(username, password, email, out errorMessage);

						this.membershipService.AddAccountToRoles(username, new String[] { Constants.Roles.SolutionAdmin });

						admin = this.ForumUserRepository.ReadOne(new ForumUserSpecifications.SpecificUsername(username));
						//admin = new ForumUser(username, email);
						//admin.SetPassword(password);
						//this.ForumUserRepository.Create(admin);

						//String key = admin.GetActivationCode();
						//admin.Activate(key);

						admin.Timezone = config.DefaultTimezone;
						admin.Culture = config.DefaultCulture;
						admin.Active = true;
						//IRepository<ForumUserSettings> userSettingsRepo = this.UnitOfWork.GetRepository<ForumUserSettings>();
						//ForumUserSettings sett = new ForumUserSettings(admin, this.config.DefaultTimezone, this.config.DefaultCulture);
						//userSettingsRepo.Create(sett);

						//IRepository<Administration> adminRepo = this.UnitOfWork.GetRepository<Administration>();
						//Administration administration = new Administration(admin);
						//adminRepo.Create(administration);

						IAccount account = this.membershipService.GetAccountByName(username);
						DependencyResolver.Current.GetService<IAuthenticationService>().SignIn(account, false);
					}

					Group guestGroup = null;
					Group memberGroup = null;
					Group moderatorGroup = null;
					Group adminGroup = null;
					IRepository<Group> groupRepo = this.Context.GetRepository<Group>();
					if (model.CreateGroups) {
						guestGroup = new Group("Guest");
						groupRepo.Create(guestGroup);

						memberGroup = new Group("Member");
						groupRepo.Create(memberGroup);

						moderatorGroup = new Group("Moderator");
						groupRepo.Create(moderatorGroup);

						adminGroup = new Group("Administrator");
						groupRepo.Create(adminGroup);

						if (model.CreateAdmin && !model.ImportMembershipUsers) {
							IRepository<GroupMember> gmRepo = this.Context.GetRepository<GroupMember>();
							GroupMember adminMember = new GroupMember(adminGroup, admin);
							gmRepo.Create(adminMember);
						}

						this.Context.SaveChanges();

						if (this.config.Editable) {
							this.config.NewUserGroups.Add(memberGroup.Id);
						}
					}

					if (model.CreateSimpleForum) {
						IRepository<Board> boardRepo = this.GetRepository<Board>();
						Board board = new Board {
							Name = "Demo Board",
							Description = "My Description",
							Disabled = false
						};

						boardRepo.Create(board);

						IRepository<AccessMask> masksRepo = this.GetRepository<AccessMask>();
						AccessMask readonlyMask = new AccessMask(board, "Readonly access", AccessFlag.Read);
						masksRepo.Create(readonlyMask);

						AccessMask readReplyMask = new AccessMask(board, "Read/reply access", AccessFlag.Read | AccessFlag.Reply);
						masksRepo.Create(readReplyMask);

						AccessMask memberMask = new AccessMask(board, "Member access", AccessFlag.Read | AccessFlag.Post
																				| AccessFlag.Reply | AccessFlag.Edit);
						masksRepo.Create(memberMask);

						AccessMask moderatorMask = new AccessMask(board, "Moderator access", AccessFlag.Read | AccessFlag.Post
																				| AccessFlag.Reply | AccessFlag.Delete
																				| AccessFlag.Edit | AccessFlag.Moderator);
						masksRepo.Create(moderatorMask);

						AccessMask adminMask = new AccessMask(board, "Admin access", AccessFlag.Read | AccessFlag.Post
																				| AccessFlag.Delete | AccessFlag.Edit
																				| AccessFlag.Moderator | AccessFlag.Poll
																				| AccessFlag.Priority | AccessFlag.Reply
																				| AccessFlag.Upload | AccessFlag.Vote);
						masksRepo.Create(adminMask);

						#region Categories
						IRepository<Category> categoryRepo = this.GetRepository<Category>();
						Category cat1 = new Category(board, "Category 1", 10);
						categoryRepo.Create(cat1);

						Category cat2 = new Category(board, "Category 2", 20);
						categoryRepo.Create(cat2);
						#endregion

						#region Forums
						IRepository<Core.Forum> forumRepo = this.GetRepository<Core.Forum>();
						Core.Forum forum1 = new Core.Forum(cat1, "Forum 1", 10, "My first forum");
						forumRepo.Create(forum1);

						Core.Forum forum2 = new Core.Forum(cat1, "Forum 2", 20, "My second forum");
						forumRepo.Create(forum2);

						Core.Forum forum3 = new Core.Forum(cat2, "Forum 3", 10, "The third forum, in the second category");
						forumRepo.Create(forum3);
						#endregion

						if (model.CreateGroups) {
							IRepository<ForumAccess> accessRepo = this.GetRepository<ForumAccess>();
							ForumAccess forumAccess = new ForumAccess(forum1, adminGroup, adminMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum1, guestGroup, readonlyMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum1, moderatorGroup, moderatorMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum1, memberGroup, memberMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum2, adminGroup, adminMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum2, guestGroup, readonlyMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum2, moderatorGroup, moderatorMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum2, memberGroup, memberMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum3, adminGroup, adminMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum3, guestGroup, readonlyMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum3, moderatorGroup, moderatorMask);
							accessRepo.Create(forumAccess);

							forumAccess = new ForumAccess(forum3, memberGroup, memberMask);
							accessRepo.Create(forumAccess);
						}
					}

					this.config.SiteURL = model.SiteURL;
					this.config.Save();

					ForumSettings settings = new ForumSettings("Version", typeof(Post).Assembly.GetName().Version.ToString());
					settingsRepo.Create(settings);

					settings = new ForumSettings("InstallDate", XmlConvert.ToString(DateTime.UtcNow, XmlDateTimeSerializationMode.Utc));
					settingsRepo.Create(settings);

					this.Context.SaveChanges();

					foreach (IInstallable installer in this.installers) {
						try {
							installer.Install();
							this.Context.SaveChanges();
						}
						catch (Exception e) { }
					}

					if (model.ImportMembershipUsers) {
						try {
							Int32 records;
							IEnumerable<IAccount> users = this.membershipService.GetAllAccounts(0, Int32.MaxValue, out records);
							IRepository<ForumUser> userRepo = this.GetRepository<ForumUser>();
							IRepository<GroupMember> gmRepo = this.GetRepository<GroupMember>();
							List<Group> newUserGroups = new List<Group>();
							foreach (Int32 id in this.config.NewUserGroups) {
								newUserGroups.Add(groupRepo.Read(id));
							}

							List<String> errors = new List<String>();
							foreach (IAccount user in users) {
								try {
									ForumUser u = new ForumUser();
									u.Culture = this.config.DefaultCulture;
									u.Deleted = false;
									u.EmailAddress = user.EmailAddress;
									u.FirstVisit = user.CreationDate;
									u.LastVisit = user.LastActivityDate;
									u.Name = user.AccountName;
									u.ProviderId = user.ProviderUserKey.ToString();
									u.Timezone = this.config.DefaultTimezone;
									u.UseFullName = false;
									u.UserFlag = UserFlag.None;

									userRepo.Create(u);
									this.Context.SaveChanges();

									foreach (Group group in newUserGroups) {
										gmRepo.Create(new GroupMember(group, u));
									}

									this.Context.SaveChanges();
								}
								catch (Exception ex) {
									errors.Add(String.Format("User {0} ({1}) with id {2} was not created", user.AccountName, user.EmailAddress, user.ProviderUserKey));
									// TODO: Collect and report back!
								}
							}
							if (errors.Count > 0) {
								// TODO: Handle this in another way??
								TempData.Add("ImportErrors", String.Join("<br />", errors));
							}
						}
						catch (Exception e) { }

						if (!String.IsNullOrWhiteSpace(model.ExistingUserEmail) && adminGroup != null) {
							ForumUser adminUser = this.ForumUserRepository.ReadOne(new ForumUserSpecifications.SpecificEmailAddress(model.ExistingUserEmail));

							IRepository<GroupMember> gmRepo = this.Context.GetRepository<GroupMember>();
							GroupMember adminMember = new GroupMember(adminGroup, adminUser);
							gmRepo.Create(adminMember);

							this.membershipService.CreateRole(Constants.Roles.BoardAdmin);
							this.membershipService.CreateRole(Constants.Roles.SolutionAdmin);

							this.membershipService.AddAccountToRoles(adminUser.Name, new String[] { Constants.Roles.SolutionAdmin });

							this.Context.SaveChanges();
						}
					}

					if (!Request.IsLocal && model.SubmitInstallation) {
						DependencyResolver.Current.GetService<IEventPublisher>().Publish<InstallationEvent>(new InstallationEvent { Version = ForumHelper.GetVersion() });
					}

					return RedirectToAction("status", "basicinstall", new { area = "forumadmin" });
				}
				catch (Exception e) { }
			}
			return View(model);
		}

		public ActionResult Status() {
			return View();
		}
	}
}